```latex
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%  Copyright by Wenliang Du                                        %%
%%  This work is licensed under the Creative Commons                %%
%%  Attribution-NonCommercial-ShareAlike 4.0 International License. %%
%%  To view a copy of this license, visit                           %%
%%  http://creativecommons.org/licenses/by-nc-sa/4.0/.              %%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

\newcommand{\commonfolder}{../../common-files}

\input{\commonfolder/header}
\input{\commonfolder/copyright}
\hypersetup{%
    pdfborder = {0 0 0}
}

\lhead{\bfseries SEED Labs -- Blockchain Exploration Lab}

\begin{document}

\begin{center}
{\LARGE 区块链探索实验室}
\end{center}

\seedlabcopyright{2023}

% *******************************************
% SECTION
% ******************************************* 
\section{概述}

本实验的目标是帮助学生获得一些与区块链的动手经验，特别是以太坊区块链。一个区块链系统相当复杂，很难在一个实验中覆盖所有方面。我们开发了一系列专注于这一新技术的实验。这是该系列的第一个实验，目的是让学生熟悉这个平台。学生们将使用现有工具并实现自己的工具来与以太坊区块链进行交互。本实验包含以下主题：

\begin{itemize}[noitemsep]
\item MetaMask、钱包和账户
\item 交易和块、发送交易
\item 以太坊节点
\end{itemize}

\paragraph{实验环境。} 本文档中的活动已在我们的预构建的 Ubuntu 20.04 虚拟机上进行测试，可以从 SEED 网站下载。

% *******************************************
% SECTION
% *******************************************
\section{实验设置：启动区块链模拟器} 

本实验将在 SEED Internet 模拟器（简称模拟器）中完成。如果这是你第一次使用模拟器，请务必阅读这一节。我们建议教师提供一个实验课程来帮助学生熟悉模拟器。

\input{../common-files/emulator.tex}

\paragraph{EtherView。}
\input{../common-files/etherview.tex}


% *******************************************
% SECTION
% *******************************************
\section{任务 1：设置 MetaMask 钱包} 

有许多方法可以与区块链进行交互。对于基本操作，我们可以使用一个钱包应用程序来管理我们的密钥、显示账户余额，并发送和接收交易。
MetaMask 是一个非常流行的以太坊区块链钱包应用程序，它既可用作浏览器扩展程序，也可用作独立的移动应用程序。
在本实验中我们将使用浏览器扩展程序。

\paragraph{任务 1.a. 安装 MetaMask 扩展。} 
在虚拟机内部，转到 Firefox 的菜单，点击“附加组件和主题”项。搜索 "metamask"，你将找到一个由 "danfinlay" 开发的扩展。
按照指示进行安装，并截图显示您已成功安装了该扩展。

\paragraph{任务 1.b. 连接到区块链。} 
我们需要通过将 MetaMask 钱包连接到区块链上的一个节点来将其与我们的区块链相连。我们可以通过运行 "docker ps" 命令找到所有以太坊节点的 IP 地址。由于实际获得的 IP 地址可能不同于以下列出的那些，请替换以下代码中的 `<IP Address>` 为实际选择的节点的 IP 地址。

\begin{lstlisting}
$ docker ps | grep Eth
e372096bb926  as150h-Ethereum-POA-00-Signer-BootNode-10.150.0.71
f0ef91ef9e22  as150h-Ethereum-POA-01-10.150.0.72
3b8c1d191058  as151h-Ethereum-POA-02-Signer-10.151.0.71
...
aea1106d932d  as164h-Ethereum-POA-18-Signer-BootNode-10.164.0.71
7cd6fa6888b2  as164h-Ethereum-POA-19-10.164.0.72
\end{lstlisting}

选择一个节点，然后在 MetaMask 中配置该节点。转到 MetaMask 的“设置”菜单并按照以下说明操作。将 `<IP Address>` 替换为实际选中的节点的 IP 地址，并截图显示您已成功连接到模拟器。

\begin{lstlisting}
设置 > 网络 > 添加网络 > 手动添加网络

网络名称: 选择任意名称（例如，SEED 模拟器）
新 RPC URL: http://<IP Address>:8545
链 ID: 1337
货币符号: ETH
\end{lstlisting}

\paragraph{任务 1.c. 添加账户。} 
在这个任务中，我们将为我们的钱包添加一些账户。我们可以让 MetaMask 创建新的账户或导入现有的账户。我们在创建模拟器时已经创建了几个账户，并且这些账户已经有了资金。这些账户是从以下短语生成的，因此可以使用相同的短语恢复它们。

\begin{lstlisting}
gentle always fun glass foster produce north tail security list example gain
\end{lstlisting}

在这个任务中，我们将将这些现有账户添加到我们的 MetaMask 钱包中，以便我们可以用它们发送交易。为此，我们需要注销我们的 MetaMask 账户（或锁定该账户）。这会将我们带到登录窗口。我们会假装忘记了密码，因此会点击“忘记密码”链接。对于 MetaMask，如果你忘记了钱包帐户的密码，除非你已经在其他地方保存了密钥，否则唯一的方法是通过秘密恢复短语来找回你的帐户密钥。
通过输入上述恢复短语，我们可以要求 MetaMask 恢复我们的密钥。

MetaMask 将生成所有具有非零余额的现有账户。请在实验报告中显示这些账户的余额。如果看不到以太币（ETH）转换为法币（例如美元），可以转到“设置 > 高级”并启用“在测试网络上显示转换”。MetaMask 现在会显示转换。

\paragraph{任务 1.d. 发送交易。} 
现在我们可以使用我们的账户发送交易。请从一个账户向另一个账户发送资金，并检查这些账户的余额以验证交易是否成功。请提供整个过程的截图。还请使用 `EtherView` 显示交易详情，并确认交易详情与您所发送的一致。

% *******************************************
% SECTION
% *******************************************
\section{任务 2：使用 Python 交互区块链} 

我们已经看过如何通过现有工具（例如 MetaMask）与区块链进行交互。在本任务中，我们将编写自己的工具来与区块链进行交互。这将使我们更深入地了解如何与区块链进行交互。我们将从主机虚拟机执行此任务。本任务所用代码可以在 `Labsetup/Files` 文件夹中找到。

\paragraph{任务 2.a：安装 Python 模块。} 
我们的程序将使用 `web3` 和 `docker` 模块。需要通过以下命令安装这些模块（它们可能已经在你的 VM 中安装了）。请注意，我们代码使用的 `web3` 是一个较旧的版本，这就是为什么我们在命令中指定了版本号。

\begin{lstlisting}
pip3 install web3==5.31.1 docker
\end{lstlisting}

\paragraph{任务 2.b：检查账户余额。} 
提供的代码显示了如何从区块链获取余额。请转到你的 MetaMask 钱包，获取前三个账户，并使用以下程序检查它们的余额。将这些值与你在 MetaMask 中看到的进行比较。

\begin{lstlisting}[caption=获取余额（`web3_balance.py`），语言=python]
#!/bin/env python3
from web3 import Web3

url  = 'http://10.150.0.71:8545'
web3 = Web3(Web3.HTTPProvider(url))  # 连接到一个区块链节点

addr = Web3.toChecksumAddress('0xF5406927254d2dA7F7c28A61191e3Ff1f2400fe9')
balance = web3.eth.get_balance(addr) # 获取余额
print(addr + ": " + str(Web3.fromWei(balance, 'ether')) + " ETH")
\end{lstlisting}

\paragraph{任务 2.c：发送交易。} 
在这个任务中，我们将使用一个 Python 程序来发出交易。这个程序构建了一个交易，用发送者的私钥对其进行签名，并通过以太坊节点发送此交易。程序将阻塞，等待确认该交易（即将其包含在一个块中）。运行此程序后，在 MetaMask 中检查发送者/接收者账户的余额是否已改变。您需要在第 1 行、2 行和 3 行填写所需的信息。对于私钥，可以从 MetaMask 获取：点击“帐户详细信息”菜单，然后单击“显示私钥”按钮。

\begin{lstlisting}[caption=发送交易（`web3_raw_tx.py`），语言=python]
from web3 import Web3
from eth_account import Account

web3 = Web3(Web3.HTTPProvider('http://ip-address:8545'))             (*@\lineone@*) 

# 发送者的私钥
key = 'replace this with the actual private key'                     (*@\linetwo@*)
sender = Account.from_key(key)

recipient = Web3.toChecksumAddress('replace this with an account #')  (*@\linethree@*)
tx = {
  'chainId':  1337,
  'nonce':    web3.eth.getTransactionCount(sender.address),
  'from':     sender.address,
  'to':       recipient,
  'value':    Web3.toWei("11", 'ether'),
  'gas':      200000,
  'maxFeePerGas':         Web3.toWei('4', 'gwei'),
  'maxPriorityFeePerGas': Web3.toWei('3', 'gwei'),
  'data':     ''
}

# 签名并发出交易
signed_tx  = web3.eth.account.sign_transaction(tx, sender.key)
tx_hash    = web3.eth.sendRawTransaction(signed_tx.rawTransaction)

# 等待交易出现在区块链上
tx_receipt = web3.eth.wait_for_transaction_receipt(tx_hash)
print("交易收据: {}".format(tx_receipt))
\end{lstlisting}

% *******************************************
% SECTION
% *******************************************
\section{任务 3：使用 Geth 交互区块链} 

我们还可以直接从区块链节点与区块链进行交互。
在我们的模拟器中，每个以太坊节点都运行着 Geth（Go-ethereum）客户端，这是一个用 Go 实现的以太坊。 
有多种方式可以与 Geth 客户端进行交互，包括 WebSocket、HTTP 和本地 IPC。当我们通过 MetaMask 或 Python 程序与 Geth 节点交互时，我们使用的是 JSON-RPC 方法。
我们也可以登录一个 Geth 节点，并使用本地 IPC 与其通信。以下 `geth` 命令在节点上获取了一个交互式控制台。

\begin{lstlisting}
root@f6fb88f9e09d / # geth attach
欢迎来到 Geth JavaScript 控制台！

实例: Geth/NODE_8/v1.10.26-stable-e5eb32ac/linux-amd64/go1.18.10
钱包地址: 0xa888497f7938825f80f35867a1e707f42b9b347d
...
要退出，请按 ctrl-d
>
\end{lstlisting}

这是一个交互式 JavaScript 控制台，因此我们可以在其中运行 JavaScript 代码。`eth` 类有许多 API 可以用来与区块链进行交互。
以下示例显示了如何获取一个账户的余额。

\begin{lstlisting}
> myaccount = "0xc20ab9a1ab88c9fae8305b302836ee7734c6afbe"
> eth.getBalance(myaccount)
100000000
\end{lstlisting}

\paragraph{任务 3.a：获取余额。} 
请获取 MetaMask 钱包中前三个账户的余额，并查看它们是否与 MetaMask 中显示的一致。

\paragraph{任务 3.b：发送交易。} 
每个节点维护着一个由其存储在 `/root/.ethereum/keystore` 目录下的账户组成的列表。这些地址加载到了 `eth.accounts[]` 数组中。
例如，要获取第一个账户的地址，可以使用 `eth.accounts[0]`。 这些帐户是锁定状态（即，用密码加密），因此为了使用它们发送交易，需要通过密码解锁它。在我们的模拟器中，所有账户都使用固定密码 "admin" 锁定。

\begin{lstlisting}
> eth.accounts
["0x3e64b5b296ccb365eab980b094a4af7b1009825e"]
> personal.unlockAccount(eth.accounts[0], "admin")
true
\end{lstlisting}

现在，我们可以从这些账户向我们的 MetaMask 钱包中的一个账户发送资金。请使用以下示例将资金发送到你的其中一个 MetaMask 钱包账户，并查看结果是否正确显示在 MetaMask 中。

\begin{lstlisting}
> sender = eth.accounts[0]
> target = "0xF5406927254d2dA7F7c28A61191e3Ff1f2400fe9"
> amount =  web3.toWei(0.2, "ether")
> eth.sendTransaction ({from: sender, to: target, value: amount})
"0x8c6c57d5a32de...7304"
\end{lstlisting}

\paragraph{任务 3.c：从不同账户发送交易。} 
而不是使用 `eth.accounts[0]`，让我们使用 MetaMask 钱包中的一个帐户，即我们将 `sender` 设置为钱包中的一部分帐户。尝试发送交易并报告观察结果。

% *******************************************
% SECTION
% *******************************************
\section{任务 4：添加全节点} 

在这个任务中，我们将学习如何加入现有的区块链网络。我们已经提供了一个空容器，并且在其名称中编码了 `new_eth_node` 这个词。
你的任务是配置此容器，将其转换为以太坊节点。首先，使用存储在创世区块中的区块链信息初始化我们的节点。一个创世块是一个区块链的第一个块。你可以从现有的以太坊节点获取创世块的副本（位于 `/tmp/eth-genesis.json`）。

\begin{lstlisting}
geth --datadir /root/.ethereum init /eth-genesis.json
\end{lstlisting}

第二步，运行 `geth` 命令加入现有的区块链网络。为此，我们需要提供一个启动节点列表。转到任何非启动节点（现有以太坊节点），你应该能够在 `/tmp` 文件夹中看到名为 `eth-node-urls` 的文件。该文件包含区块链网络上的启动节点列表。复制并粘贴其内容到 `new_eth_node` 容器中的 `/tmp/eth-node-urls` 文件中。在下面的 `geth` 命令中，我们将传递给 `-bootnodes` 选项 `/tmp/eth-node-urls` 文件的内容。

\begin{lstlisting}[caption=\texttt{start.sh}]
geth --datadir /root/.ethereum --identity="NEW_NODE_01" --networkid=1337 \
     --syncmode full --snapshot=False --verbosity=2  --port 30303 \
     --bootnodes "$(cat /tmp/eth-node-urls)" --allow-insecure-unlock \      
     --http --http.addr 0.0.0.0 --http.corsdomain "*" \
     --http.api web3,eth,debug,personal,net,clique,engine,admin,txpool
\end{lstlisting}

\paragraph{任务。}请按照上述说明将 `new_eth_node` 容器转换为一个以太坊节点。设置好之后，请执行以下任务：

\begin{itemize}
\item 在该节点上运行 "geth attach" 以获取 JavaScript 控制台；使用 `admin.peers` 查看该节点的对等节点。

\item 同一控制台上，使用 `personal.newAccount()` 在此节点上创建一个新账户。我们称它为账户 Z。

\item 修改任务 2 中的 Python 代码（`web3_raw_tx.py`），使其连接到这个新节点，并使用该新节点发送交易。向账户 Z 发送一些以太币。

\item 在 JavaScript 控制台上，从账户 Z 向另一个账户发送一个交易。
\end{itemize}

% *******************************************
% SECTION
% ******************************************* 
\section{提交}

\seedsubmission

\end{document}
```